#!/usr/bin/env python
# encoding: utf-8

# @Author  : vfengcc
# @File    : fn.py
# @Time    : 2020/2/12 12:26

'''
    简单用函数实现一下map,reduce,fileter等函数
    map:
        def fn(x):
            return x * x
        ret = list(map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
        str1 = list(map(str,[1, 2, 3, 4, 5, 6, 7, 8, 9]))
    reduce:
        def fn(x, y):
            return x + y
        ret = reduce(fn, [1, 2, 3, 7, 9])
    filter:
        def fn(n):
            return n % 2 == 1
        ret = list(filter(fn, [1, 2, 3, 4, 5, 6, 9]))

    提示： map,reduce,filter一般都是接收两个参数(fn, Iterable)
    要求:
        1.实现的函数返回结果时，可以不为惰性
        2.检测Iterable是否为可迭代对象，如果不可迭代，抛出异常"Iterable not is Iterable"
'''

from collections.abc import Iterable

MSG_DISABLEITER = 'Iterable not is Iterable'


def checkiterable(fn):
    def warpper(*args):
        _items = args[1]
        if not isinstance(_items, Iterable):
            # 抛出异常"Iterable not is Iterable"
            raise TypeError(MSG_DISABLEITER)
        else:
            return fn(*args)
    return warpper


def fn_map_int(x):
    return int(x) * int(x)


def fn_map_str(x):
    return '【{}】'.format(str(x))


def fn_reduce(x, y):
    return x + y


def fn_filter(n):
    return n % 2 == 1


@checkiterable
def my_map(fn, items):
    for item in items:
        yield fn(item)


@checkiterable
def my_reduce(fn, items, initializer=0):
    total = initializer
    for item in items:
        total = fn(item, total)
    return total


@checkiterable
def my_filter(fn, items):
    items_new = []
    for item in items:
        if fn(item):
            items_new.append(item)
    return items_new


if __name__ == '__main__':
    print('{:*^100}'.format('测试map'))
    test_datas = [123121212123123123, [1, 2, 3, 4, 5, 6], ['ab', 'cd', 'ef']]
    import random
    for item in test_datas:
        map_fn = random.choice([fn_map_int, fn_map_str])
        print('\t{} {:>10} {} 结果：'.format(item, '数据类型' + type(item).__name__, map_fn.__name__), end='\t')
        try:
            print('\t\t{}'.format(list(my_map(map_fn, item))))
        except Exception as e:
            print('\t\t{}'.format(e))

    print('{:*^100}'.format('测试reduce'))
    test_datas = [[1, 2, 3, 4, 5], 12345]
    for item in test_datas:
        try:
            print('\t{} reduce 结果:{}'.format(item, my_reduce(fn_reduce, item)))
            print('\t{} reduce（带initializer） 结果:{}'.format(item, my_reduce(fn_reduce, item, 100)))
        except Exception as e:
            print('\t{} reduce 结果:{}'.format(item, e))

    print('{:*^100}'.format('测试filter'))
    test_datas = [[1, 2, 3, 4, 5, 6, 9], 12345]
    for item in test_datas:
        try:
            print('\t{} filter 结果:{}'.format(item, my_filter(fn_filter, item)))
        except Exception as e:
            print('\t{} filter 结果:{}'.format(item, e))

    # 装饰器的写法很赞～
